"""
@Author：十
@Time：2024/12/5 10:07
@FileName：main.py
@Description：主窗口及逻辑
"""
import csv
import os
import sqlite3
import sys
import threading
import time
from configparser import ConfigParser
from datetime import datetime

from PyQt5.QtCore import QTimer, pyqtSignal, Qt, QDate, QTime, QDateTime, QEvent, QPropertyAnimation, QRect
from PyQt5.QtGui import QIcon
from PyQt5.QtSerialPort import QSerialPortInfo
from PyQt5.QtWidgets import QWidget, QApplication, QMessageBox, QLineEdit, QTableWidgetItem, QTreeWidgetItem, QTreeWidget, QVBoxLayout
from loguru import logger
import pyqtgraph as pg

import loggingConfig
from SerialThread import SerialThread
from UI.main import Ui_Form
from details import DetailsWindow
from exception import ExceptionWindow
from filter import FilterWindow
from settings import SettingWindow
from diagnose import Diagnose


class MainWindow(QWidget, Ui_Form):
    # 获取选择的comboBOX的传感器ID
    Signal_send_temperature_date = pyqtSignal(object)

    # 转发以获取的温度数据
    Signal_obtained_temperature_date = pyqtSignal(object)
    # 转发以获取的设备数据
    Signal_obtained_device_data = pyqtSignal(object)
    # 获取筛选边栏的数据
    Signal_Get_filter_parameters = pyqtSignal(object)

    def __init__(self):

        super(MainWindow, self).__init__()
        self.external_plot = None
        self.internal_plot = None
        self.is_save_scv = False
        # 环境温度传感器ID
        self.ambient_sensor = None
        # 缓存温度数据
        self.cached_temperature = None
        # 缓存设备数据
        self.cached_device = None

        self.Serial_QThread_Function = None
        self.setupUi(self)

        # 日志初始化
        loggingConfig.setup_logging()

        # 诊断类
        self.diagnose = Diagnose()

        # 初始化串口
        self.serial_port = None
        # 用于跟踪是否正在发送报文
        self.sending = False

        # 串口锁
        self.serial_lock = threading.Lock()

        # 传递筛选边栏的数据
        # self.Signal_Get_filter_parameters.connect(self.filter_parameters)

        # 呼出设置界面
        self.pushButton_Settings.clicked.connect(self.show_sittingWindow)

        # 点击仪表盘异常，弹出异常窗口
        self.label_13.installEventFilter(self)
        self.label_Excep.installEventFilter(self)

        # 温度数据按钮状态
        self.pushButton_Roport.clicked.connect(self.change_save_flag)
        # 呼出筛选边栏
        self.pushButton_Filter.clicked.connect(self.show_filterSidebar)
        self.sidebar = FilterWindow(self)
        self.sidebar.setGeometry(-250, 0, 250, self.height())
        # 创建主容器（包含所有内容）
        self.main_container = QWidget(self)
        self.main_container.setGeometry(0, 0, self.width(), self.height())

        # 将主界面内容移动到主容器中
        layout = QVBoxLayout(self.main_container)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.widget_12)  # 顶部工具栏
        layout.addWidget(self.widget)  # 主内容区域
        layout.addWidget(self.label_Buttom)  # 底部标签
        # 动画设置
        self.sidebar_animation = QPropertyAnimation(self.sidebar, b"geometry")
        self.sidebar_animation.setDuration(300)

        self.container_animation = QPropertyAnimation(self.main_container, b"geometry")
        self.container_animation.setDuration(300)

        self.sidebar_visible = False

        # 点击Tree选择设备，刷新数据表格
        self.treeWidget.itemClicked.connect(self.device_tree_selected)

        # 接收到温度数据后保存
        self.Signal_send_temperature_date.connect(self.insert_temperature_date)

        # 点击站点表格弹出详情界面并传递点击的一行数据
        self.tableWidget.cellClicked.connect(self.show_detailsWindow)

        # 定时器设置
        self.timer = QTimer(self)

        # # 串口定时发送线程
        # # 串口线程
        self.Serial_Thread = None

        self.create_DB()

        self.init_interface()
        # self.start_sending()

        # 初始化蒙版
        self.init_overlay()

        # 树节点默认第一个叶节点
        self.refresh_temperature_table()

        self.start_sending()

    def resource_path(self, relative_path):
        try:
            base_path = sys._MEIPASS
        except Exception:
            base_path = os.path.abspath(".")

        return os.path.join(base_path, relative_path)

    def init_interface(self):
        # 隐藏标题栏
        self.setWindowFlag(Qt.FramelessWindowHint)

        self.select_device()

        self.tableWidget.setColumnWidth(5, 500)
        # self.tableWidget.setColumnWidth(4, )

        # 表头排序
        self.tableWidget.setSortingEnabled(True)

        self.pushButton_Filter.setVisible(False)

        self.pushButton_Home.setVisible(False)
        self.pushButton_Roport.setVisible(False)

        # 仪表盘
        self.show_meter()

    # 呼出设置界面
    def show_sittingWindow(self):
        try:
            self.settingWindow = SettingWindow()
            self.settingWindow.showWindow()

            self.settingWindow.finished.connect(self.refresh_ininterface)
        except Exception as e:
            logger.critical(f'呼出配置窗口错误：{e}')
            print('呼出配置窗口错误:', e)

    # 呼出接头温度数据界面
    def show_detailsWindow(self, row, column):
        try:
            # 获取该行的所有列数据
            row_data = []
            for col in range(self.tableWidget.columnCount()):
                item = self.tableWidget.item(row, col)
                if item is not None:
                    row_data.append(item.text())
                else:
                    row_data.append("")

            # 创建详情窗口并传递数据
            self.detailsWindow = DetailsWindow()
            self.detailsWindow.set_data(row_data)
            # 显示蒙版
            self.show_overlay()
            self.detailsWindow.show()

            self.detailsWindow.finished.connect(self.hide_overlay)

        except Exception as e:
            logger.critical(f'呼出详情窗口错误：{e}')

    # 点击仪表盘异常数据呼出异常传感器窗口
    def eventFilter(self, obj, event):
        if obj == self.label_13 or obj == self.label_Excep:
            if event.type() == QEvent.MouseButtonPress:
                self.show_exceptionWindow()
                return True
        return super().eventFilter(obj, event)

    def show_exceptionWindow(self):
        try:
            self.exceptionWindow = ExceptionWindow()
            self.exceptionWindow.showWindow()
        except Exception as e:
            logger.critical(f'呼出异常窗口错误：{e}')

    def refresh_ininterface(self):
        self.show_meter()
        self.refresh_temperature_table()

    # 接头状态仪表"接头总数"
    def show_meter(self):
        try:
            conn = sqlite3.connect(self.resource_path('db/WTC.db'))
            cursor = conn.cursor()

            # 接头总数
            # cursor.execute("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", ('temperature_db',))
            count_sensor_sum = """select count(*) from device_db;"""
            cursor.execute(count_sensor_sum)
            count_sum = cursor.fetchone()[0]

            # 正常接头数
            cursor.execute("select count(*) from device_db where state = '正常';")
            count_normal = cursor.fetchone()[0]

            self.label_Sum.setText(str(count_sum))
            self.label_Normal.setText(str(count_normal))
            self.label_Excep.setText(str(count_sum - count_normal))

        except sqlite3.Error as e:
            logger.critical(f'创建数据库错误：{e}')

        finally:
            # 确保关闭连接
            if conn:
                conn.close()

    # 呼出筛选边框
    def show_filterSidebar(self):
        try:
            sidebar_width = 500

            if self.sidebar_visible:
                # 隐藏侧边栏，主容器向右移动恢复原状
                self.sidebar_animation.setEndValue(QRect(self.width(), 0, sidebar_width, self.height()))
                self.container_animation.setEndValue(QRect(0, 0, self.width(), self.height()))
            else:
                # 显示侧边栏，主容器向左压缩
                self.sidebar_animation.setEndValue(QRect(self.width() - sidebar_width, 0, sidebar_width, self.height()))
                self.container_animation.setEndValue(QRect(0, 0, self.width() - sidebar_width, self.height()))

            self.sidebar_visible = not self.sidebar_visible
            self.sidebar_animation.start()
            self.container_animation.start()
        except Exception as e:
            logger.critical(f'呼出筛选窗口错误：{e}')

    def resizeEvent(self, event):
        try:
            # 调整主容器大小
            sidebar_width = 500

            if self.sidebar_visible:
                # 主容器向左压缩
                self.main_container.setGeometry(0, 0, self.width() - sidebar_width, self.height())
                self.sidebar.setGeometry(self.width() - sidebar_width, 0, sidebar_width, self.height())
            else:
                self.main_container.setGeometry(0, 0, self.width(), self.height())
                self.sidebar.setGeometry(self.width(), 0, sidebar_width, self.height())

                # 确保内容区域适应新的宽度
            self.tableWidget.resizeColumnsToContents()

            super().resizeEvent(event)
            if hasattr(self, 'overlay'):
                self.overlay.setGeometry(self.rect())
        except Exception as e:
            print(e)
            logger.critical(f'调整主容器大小错误：{e}')

    # 创建数据库
    def create_DB(self):
        try:
            # 连接数据库（如果不存在会自动创建）
            conn = sqlite3.connect(self.resource_path('db/WTC.db'))
            cursor = conn.cursor()

            # 检查库是否存在，不存在时回自动创建库
            cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='temperature_db';")

            # 检查温度表是否存在
            cursor.execute("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", ('temperature_db',))
            if not cursor.fetchone():
                create_table_sql_device = """
                    CREATE TABLE temperature_db (
                        id INTEGER PRIMARY KEY AUTOINCREMENT,
                        timestamp DATETIME NOT NULL,
                        inner_temperature REAL NOT NULL,
                        external_temperature REAL NOT NULL,
                        ambient_temps REAL NOT NULL,
                        sensor_id TEXT NOT NULL
                    );
                    """
                cursor.execute(create_table_sql_device)
                print("温度表创建成功")

            # 检查设备表是否存在
            cursor.execute("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", ('device_db',))
            if not cursor.fetchone():
                create_table_sql_tempt = """
                    CREATE TABLE device_db (
                        id INTEGER PRIMARY KEY AUTOINCREMENT,
                        site TEXT NOT NULL,
                        location TEXT NOT NULL,
                        device TEXT NOT NULL,
                        joints TEXT NOT NULL,
                        sensor_id INTEGER NOT NULL,
                        state TEXT NOT NULL
                    );
                    """
                cursor.execute(create_table_sql_tempt)
                print("设备表创建成功")

                conn.commit()

        except sqlite3.Error as e:
            logger.critical(f'创建数据库错误：{e}')

        finally:
            # 确保关闭连接
            if conn:
                conn.close()

    def toggle_send_message(self):
        if self.sending and self.Serial_Thread.running:
            self.stop_sending()
        else:
            self.start_sending()

    # 刷新数据表格，tree获得当前item，没有则是第一个item
    def refresh_temperature_table(self):
        item = self.get_current_tree_item()
        # TODO:还要获取筛选条件
        self.device_tree_selected(item)
        # 数据库查所有device下的joint状态，以更新指示灯
        self.query_device_status()

    # 开始发送前读取串口数据
    def start_sending(self):
        port = None
        timeout = None
        address = None
        try:
            config = ConfigParser()
            config.read('config.ini')

            port = config.get("Connection", "port")
            timeout = config.getint("Connection", "timeout")
            address = config.getint("Connection", "address")
            ambient_sensor = config.getint("AmbientSensor", "ambient_sensor")
            self.ambient_sensor = ambient_sensor
            if hasattr(self, 'Serial_Thread') and self.Serial_Thread:
                self.stop_sending()

            self.Serial_Thread = SerialThread(port, 9600)

            # 连接通用数据接收信号
            self.Serial_Thread.data_received.connect(self.receive_data)

            # 连接MODBUS特定功能码信号
            # self.Serial_Thread.modbus_read_coils_response.connect(self.handle_read_coils)
            # self.Serial_Thread.modbus_read_discrete_inputs_response.connect(self.handle_read_discrete_inputs)
            # self.Serial_Thread.modbus_read_holding_registers_response.connect(self.handle_read_holding_registers)
            # self.Serial_Thread.modbus_read_input_registers_response.connect(self.handle_read_input_registers)
            # self.Serial_Thread.modbus_write_single_coil_response.connect(self.handle_write_single_coil)
            # self.Serial_Thread.modbus_write_single_register_response.connect(self.handle_write_single_register)
            # self.Serial_Thread.modbus_write_multiple_coils_response.connect(self.handle_write_multiple_coils)
            # self.Serial_Thread.modbus_write_multiple_registers_response.connect(self.handle_write_multiple_registers)

            self.Serial_Thread.modbus_read_temperature.connect(self.parse_temperature_data)
            self.Serial_Thread.modbus_read_device.connect(self.parse_device_data)

            # 连接超时信号
            self.Serial_Thread.modbus_response_timeout.connect(self.handle_modbus_timeout)

            # 连接串口状态信号
            self.Serial_Thread.serial_error.connect(lambda msg: (
                self.label_Comm.setText("连接失败"),
                self.label_Comm.setStyleSheet('color: red')
            ))
            self.Serial_Thread.serial_success.connect(lambda msg: (
                self.label_Comm.setText("连接成功"),
                self.label_Comm.setStyleSheet('color: green')
            ))

            self.Serial_Thread.start()

        except Exception as e:
            print("获取设置错误：", e)
            logger.critical(f'获取设置错误：{e}')
            QMessageBox.warning(self, "获取设置错误：" + str(e), str(e))

        try:
            if timeout is not None:
                # 定时发送数据，添加期望的响应信息
                self.timer.timeout.connect(lambda: self.Serial_Thread.send_data(
                    bytes.fromhex("010305DD0078D51E"),
                    immediate=False,
                    expected_response=(0x03, 0x05DD)  # 功能码0x03，地址0x05DD
                ))

                self.timer.start(timeout)
        except Exception as e:
            print("定时打开串口错误：", e)
            logger.critical('打开串口错误：', e)

    def handle_read_coils(self, data):
        """处理读线圈响应"""
        print(f"收到读线圈响应: {data.hex()}")
        # 根据需要处理读线圈数据
        self.receive_data(data)

    def handle_read_discrete_inputs(self, data):
        """处理读离散输入响应"""
        print(f"收到读离散输入响应: {data.hex()}")
        # 根据需要处理读离散输入数据
        self.receive_data(data)

    def handle_read_holding_registers(self, data):
        """处理读保持寄存器响应"""
        print(f"收到读保持寄存器响应: {data.hex()}")
        # 解析读保持寄存器数据
        self.parse_temperature_data(data)

    def handle_read_input_registers(self, data):
        """处理读输入寄存器响应"""
        print(f"收到读输入寄存器响应: {data.hex()}")
        # 根据需要处理读输入寄存器数据
        self.receive_data(data)

    def handle_write_single_coil(self, data):
        """处理写单个线圈响应"""
        print(f"收到写单个线圈响应: {data.hex()}")
        # 根据需要处理写单个线圈响应

    def handle_write_single_register(self, data):
        """处理写单个寄存器响应"""
        print(f"收到写单个寄存器响应: {data.hex()}")
        # 根据需要处理写单个寄存器响应

    def handle_write_multiple_coils(self, data):
        """处理写多个线圈响应"""
        print(f"收到写多个线圈响应: {data.hex()}")
        # 根据需要处理写多个线圈响应

    def handle_write_multiple_registers(self, data):
        """处理写多个寄存器响应"""
        print(f"收到写多个寄存器响应: {data.hex()}")
        # 根据需要处理写多个寄存器响应

    def handle_modbus_timeout(self, func_code, address):
        """处理MODBUS响应超时"""
        print(f"MODBUS响应超时 - 功能码: {func_code}, 地址: 0x{address:04X}")
        QMessageBox.warning(self, "MODBUS超时", f"等待功能码{func_code}，地址0x{address:04X}的响应超时")
        # 可以在这里添加超时后的处理逻辑

    # 解析收到的设备数据
    def parse_device_data(self, raw_hex):
        try:
            hex_str = raw_hex.hex()
            frame_bytes = bytes.fromhex(hex_str)
            if len(frame_bytes) < 6:
                raise ValueError("无效报文长度")

            parsed = {
                "address": frame_bytes[0],
                "function_code": frame_bytes[1],
                "data": [],
                "crc_received": int.from_bytes(frame_bytes[-2:], 'little'),
                "crc_valid": False
            }

            # CRC校验计算
            crc_calculated = self.modbus_crc(frame_bytes[:-2])
            parsed["crc_valid"] = (crc_calculated == parsed["crc_received"])

            # 数据域解析（从第4字节开始）
            data_part = frame_bytes[3:-2]  # 第4字节到倒数第3字节
            for i in range(0, len(data_part), 2):
                if i + 1 >= len(data_part): break
                parsed["data"].append(
                    self.analyse_device_data(data_part[i], data_part[i + 1])
                )

            index = 0
            device_data = []
            for idx, data in enumerate(parsed['data']):
                value = data['decimal']
                if value == 3276.7:
                    value = None
                if value == 0:
                    break

                device_data.append(value)

                index += 1

            print("设备数据：", device_data)
            # 将解析结果传递给统一处理函数
            if not all(element is None for element in device_data):
                self.handle_parsed_data('device', device_data)

        except Exception as e:
            print("解析设备数据错误：", e)
            logger.critical('解析设备数据错误：', e)

    # 解析接收到的温度数据
    def parse_temperature_data(self, raw_hex):
        try:
            hex_str = raw_hex.hex()
            # print(hex_str)

            frame_bytes = bytes.fromhex(hex_str)

            if len(frame_bytes) < 6:
                raise ValueError("无效报文长度")

            parsed = {
                "address": frame_bytes[0],
                "function_code": frame_bytes[1],
                "data": [],
                "crc_received": int.from_bytes(frame_bytes[-2:], 'little'),
                "crc_valid": False
            }

            # CRC校验计算
            crc_calculated = self.modbus_crc(frame_bytes[:-2])
            parsed["crc_valid"] = (crc_calculated == parsed["crc_received"])

            # 数据域解析（从第4字节开始）
            data_part = frame_bytes[3:-2]  # 第4字节到倒数第3字节
            for i in range(0, len(data_part), 2):
                if i + 1 >= len(data_part): break
                parsed["data"].append(
                    self.analyse_temperature_data(data_part[i], data_part[i + 1])
                )

        except Exception as e:
            print("接收温度数据错误：", e)
            logger.critical('接收温度数据错误：', e)

        try:
            index = 0
            temperature_data = []
            for idx, data in enumerate(parsed['data']):
                value = data['decimal']
                if value == 3276.7:
                    value = None
                if value == 0:
                    break

                temperature_data.append(value)

                index += 1

            # print('temperature:', temperature_data)

            self.Signal_obtained_temperature_date.emit(parsed)

            # 有温度结果再问设备信息，上面的传感器没连的时候测试用
            if not all(element is None for element in temperature_data):
                # 以获取温度数据，转发到组合函数
                self.handle_parsed_data('temperature', temperature_data)
                self.Serial_Thread.send_data(
                    bytes.fromhex("0103044D0078D4CF"),
                    immediate=False,
                    expected_response=(0x03, 0x044D))

        except Exception as e:
            print("展示数据错误：", e)
            logger.critical(f'下拉框展示数据错误：{e}')

    # 统一处理解析后的数据
    def handle_parsed_data(self, data_type, parsed_data):
        """
            data_type: 数据类型 ('temperature', 'device', 'other')
            parsed_data: 解析后的数据字典
        """
        try:
            if data_type == 'temperature':
                # print('接收到的温度数据：', parsed_data)
                self.cached_temperature = parsed_data
                self.merge_data()

            elif data_type == 'device':
                # print('接收到的设备数据：', parsed_data)
                self.cached_device = parsed_data
                self.merge_data()
            else:
                # print(f"处理其他数据: {parsed_data}")
                # 处理其他类型数据
                self.process_other_data(parsed_data)

        except Exception as e:
            logger.error(f"处理解析数据时出错: {e}")

    # 合并温度和设备数据
    def merge_data(self):
        if self.cached_temperature is not None and self.cached_device is not None:
            merged_data = []
            for temp, device in zip(self.cached_temperature, self.cached_device):
                if temp is not None:
                    merged_data.append((temp, device))
                    # TODO: 这里还没判断正常or异常
                    self.update_device_status(device, "正常")
                # 如果温度数据为空，更新设备状态为“离线”
                else:
                    self.update_device_status(device, "离线")
            # 存入数据库
            self.insert_temperature_date(merged_data)
            # 刷新界面
            self.refresh_temperature_table()
            # 在 merge_data 方法中添加
            if self.cached_temperature is not None:
                # 即使没有设备数据也尝试刷新
                self.refresh_temperature_table()

            # 如果detailsWindow打开，向detailsWindow发送信号，刷新界面
            # 判断 detailsWindow 是否打开
            if hasattr(self, 'detailsWindow') and self.detailsWindow and self.detailsWindow.isVisible():
                self.detailsWindow.Signal_refresh_interface.emit()

            self.cached_temperature = None
            self.cached_device = None

    # 解析接收到的数据
    def receive_data(self, raw_hex):
        try:
            hex_str = raw_hex.hex()
            # print(hex_str)
            frame_bytes = bytes.fromhex(hex_str)

            if len(frame_bytes) < 6:
                raise ValueError("无效报文长度")

            parsed = {
                "address": frame_bytes[0],
                "function_code": frame_bytes[1],
                "data": [],
                "crc_received": int.from_bytes(frame_bytes[-2:], 'little'),
                "crc_valid": False
            }

            # CRC校验计算
            crc_calculated = self.modbus_crc(frame_bytes[:-2])
            parsed["crc_valid"] = (crc_calculated == parsed["crc_received"])

            # 数据域解析（从第4字节开始）
            data_part = frame_bytes[3:-2]
            for i in range(0, len(data_part), 2):
                if i + 1 >= len(data_part): break
                parsed["data"].append(
                    self.analyse_data(data_part[i], data_part[i + 1])
                )
        except Exception as e:
            print("接收数据错误：", e)
            logger.critical('接收数据错误：', e)

        # 数据展示
        try:
            index = 0
            temperature_data = []
            for idx, data in enumerate(parsed['data']):
                value = data['decimal']
                if value == 3276.7:
                    value = None
                if value == 0:
                    break

                temperature_data.append(value)

                index += 1

                # print('temperature:', temperature_data)
            self.Signal_obtained_temperature_date.emit(temperature_data)

        except Exception as e:
            print("展示数据错误：", e)
            logger.critical(f'下拉框展示数据错误：{e}')

    # 树状图设备选择列表
    def device_tree_selected(self, item):
        try:
            conn = sqlite3.connect(self.resource_path('db/WTC.db'))
            cursor = conn.cursor()

            if item.childCount() == 0:
                ancestors = self.get_ancestors(item)
                device = [ancestor.text(0) for ancestor in ancestors]
                # print(device)
            else:
                item.setExpanded(not item.isExpanded())
                return

            select = "SELECT sensor_id FROM device_db where site = ? and  location = ? and device = ?"

            cursor.execute(select, (*device,))

            result = cursor.fetchall()
            data = [data[0] for data in result]

            self.show_joints_table(data)

        except Exception as e:
            logger.critical(f'点击树状图节点错误：{e}')
        finally:
            if conn:
                conn.close()

    # 获取Tree祖先节点
    def get_ancestors(self, item):
        ancestors = []
        current = item

        while current:
            ancestors.append(current)
            current = current.parent()

        return ancestors[::-1]

    # tableWidget获取当前节点。如果没有当前节点，则选定第一个叶节点
    def get_current_tree_item(self):
        # 获取当前选中的节点
        selected_items = self.treeWidget.selectedItems()

        if selected_items:
            current_item = selected_items[0]
            # 如果当前节点是叶节点，直接返回
            if current_item.childCount() == 0:
                return current_item
            # 否则查找当前节点下的第一个叶节点
            return self.find_first_leaf(current_item)
        else:
            # 没有选中节点，从根节点开始查找第一个叶节点
            return self.find_first_leaf(self.treeWidget)

    # 递归查找Tree第一个叶节点（深度优先搜索）
    def find_first_leaf(self, item_or_widget):
        # 如果是树控件，从顶级项目开始
        if isinstance(item_or_widget, QTreeWidget):
            for i in range(item_or_widget.topLevelItemCount()):
                leaf = self.find_first_leaf(item_or_widget.topLevelItem(i))
                if leaf:
                    return leaf
            return None

        # 如果是树节点
        item = item_or_widget
        # 如果是叶节点（没有子节点），直接返回
        if item.childCount() == 0:
            return item

        # 否则递归查找子节点中的第一个叶节点
        for i in range(item.childCount()):
            child = item.child(i)
            leaf = self.find_first_leaf(child)
            if leaf:
                return leaf

        return None

    # 参数过滤
    def filter_parameters(self, parameters):
        try:
            conn = sqlite3.connect(self.resource_path('db/WTC.db'))
            cursor = conn.cursor()

            # 设备状态
            if parameters['operating'] == '全部':
                parameters['operating'] = ('正常', '异常', '离线')
            elif parameters['operating'] == '正常':
                parameters['operating'] = ('正常',)
            elif parameters['operating'] == '离场':
                parameters['operating'] = ('离场',)
            elif parameters['operating'] == '异常':
                parameters['operating'] = ('异常',)

            select_temperature_filter = """
                SELECT
                    device_db.joints,
                    temperature_db.sensor_id,
                    temperature_db.inner_temperature,
                    temperature_db.external_temperature,
                    temperature_db.ambient_temps,
                    temperature_db.timestamp,
                    device_db.state
                FROM device_db 
                LEFT JOIN temperature_db ON device_db.sensor_id = temperature_db.sensor_id
                WHERE temperature_db.sensor_id = ?
                AND timestamp BETWEEN ? AND ?
                AND inner_temperature BETWEEN ? AND ?
                AND external_temperature BETWEEN ? AND ?
                AND ambient_temps BETWEEN ? AND ?
                AND state = ?
                ORDER BY temperature_db.timestamp DESC 
                LIMIT 1;
                """

            # TODO: sensor_id从哪里来

            #
            # cursor.execute(select_temperature_filter, (sensor_id, *parameters,))
            #
            # result = cursor.fetchall()



        except Exception as e:
            logger.critical(f'参数过滤错误：{e}')
            print(f'参数过滤错误：{e}')

    # 表格展示接头数据
    def show_joints_table(self, joints):
        self.tableWidget.setSortingEnabled(True)
        self.tableWidget.setRowCount(0)

        try:
            conn = sqlite3.connect(self.resource_path('db/WTC.db'))
            cursor = conn.cursor()
            # 显示站点及最新一条的温度数据

            select_joints_temperature = """
            SELECT
                device_db.joints,
                temperature_db.sensor_id,
                temperature_db.inner_temperature,
                temperature_db.external_temperature,
                temperature_db.ambient_temps,
                temperature_db.timestamp,
                device_db.state
            FROM device_db 
            LEFT JOIN temperature_db ON device_db.sensor_id = temperature_db.sensor_id
            WHERE temperature_db.sensor_id = ?
            ORDER BY temperature_db.timestamp DESC 
            LIMIT 1;
            """

            for joint in joints:
                cursor.execute(select_joints_temperature, (joint,))
                temperature_datas = cursor.fetchall()
                # print(temperature_datas)

                start_row = self.tableWidget.rowCount()

                table_position = self.tableWidget.rowCount() + len(temperature_datas)
                self.tableWidget.setRowCount(table_position)

                for row_idx, row_data in enumerate(temperature_datas):
                    for col_idx, cell_data in enumerate(row_data):
                        item = QTableWidgetItem(str(cell_data))
                        item.setTextAlignment(Qt.AlignCenter)
                        self.tableWidget.setItem(start_row + row_idx, col_idx, item)
                        if col_idx == 6 and cell_data not in ["正常", "环境"]:
                            item.setForeground(Qt.red)
                        elif col_idx == 6 and cell_data in ["正常", "环境"]:
                            item.setForeground(Qt.green)

                self.tableWidget.setSortingEnabled(True)

        except Exception as e:
            logger.critical(f'填充数据展示表错误：{e}')

        finally:
            if conn:
                conn.close()

    # 温度数据插入数据库
    def insert_temperature_date(self, date):
        conn = None

        temperature_data = {
            'external_temperature': None,
            'inner_temperature': None,
            'ambient_temps': None,
            'sensor_id': None,
            'timestamp': None,
            'state': None
        }

        try:
            # print('接收到的数据是：', date)

            conn = sqlite3.connect(self.resource_path('db/WTC.db'))
            cursor = conn.cursor()
            current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

            # 查询传感器ID
            select_sensorID = """ SELECT sensor_id FROM device_db"""
            cursor.execute(select_sensorID)
            # result = cursor.fetchone()
            # print(result)
            insert_tempt = """
                            INSERT INTO temperature_db (timestamp, inner_temperature, external_temperature, sensor_id, ambient_temps) VALUES (?, ?, ?, ?, ?)
                        """

            ambient_index, remaining_temperatures = self.find_ambient_temperature(date, self.ambient_sensor)
            ambient_temperature = date[ambient_index][0]
            # print('环境温度：', ambient_temperature)
            # print('剩余温度：', remaining_temperatures)

            for external_temperature, sensor_id in remaining_temperatures:
                temperature_data['external_temperature'] = external_temperature
                # temperature_data['inner_temperature'] = round(self.diagnose.load_and_predict([ambient_temperature, external_temperature, 500]), 2)
                temperature_data['state'] = self.diagnose.diagnose([ambient_temperature, external_temperature])[1]

                # print('诊断结果：', round((self.diagnose.diagnose([ambient_temperature, external_temperature])[0]), 2))

                # temperature_data['inner_temperature'] = round(self.diagnose.load_and_predict([ambient_temperature, external_temperature], 'model_without_current.pkl'), 2)
                temperature_data['inner_temperature'] = round((self.diagnose.diagnose([ambient_temperature, external_temperature])[0]), 2)

                temperature_data['ambient_temps'] = ambient_temperature
                temperature_data['sensor_id'] = sensor_id
                temperature_data['timestamp'] = current_time

                print(temperature_data)

                # 保存到数据库
                cursor.execute(insert_tempt, (
                    temperature_data['timestamp'], temperature_data['inner_temperature'], temperature_data['external_temperature'], temperature_data['sensor_id'],
                    temperature_data['ambient_temps']))

            conn.commit()

            # # TODO: 主机读传感器ID。与数据库传感器ID不一致，则进行提醒
            # sensor_IDs = self.send_get_address()

            # 保存到表格
            if self.is_save_scv:
                self.save_temp_date(current_time, external_temperature)

        except Exception as e:
            logger.critical(f'插入温度数据错误：{e}')
            print('插入温度数据错误:', e)

        finally:
            # 确保关闭连接
            if conn:
                conn.close()

    # 从传递的二维数组中找环境温度值
    def find_ambient_temperature(self, arrays, target):
        try:
            index = next(i for i, array in enumerate(arrays) if target in array)
            remaining_arrays = [array for i, array in enumerate(arrays) if i != index]
            return index, remaining_arrays
        except StopIteration:
            return -1, arrays

    # 从数据库获取设备信息
    def select_device(self):
        try:
            conn = sqlite3.connect(self.resource_path('db/WTC.db'))
            cursor = conn.cursor()

            read_table_device = """
                         SELECT site, location, device, joints FROM device_db;
                        """

            cursor.execute(read_table_device)

            device_data = cursor.fetchall()

            self.show_device_tree(device_data)

        except Exception as e:
            logger.critical(f'获取设备列表错误：{e}')
            print('获取设备列表错误:', e)
        finally:
            if conn:
                conn.close()

    # 树状图展示
    def show_device_tree(self, data):
        try:
            hierarchy = {}
            self.treeWidget.clear()

            for record in data:
                site, point, device, connector = record

                # 初始化站点
                if site not in hierarchy:
                    hierarchy[site] = {
                        'item': QTreeWidgetItem([site]),
                        'children': {}
                    }
                    hierarchy[site]['item'].setData(0, Qt.UserRole, [site])
                    self.treeWidget.addTopLevelItem(hierarchy[site]['item'])
                    hierarchy[site]['item'].setIcon(0, QIcon(":/icon/unfold.png"))

                # 初始化站点下的点位
                if point not in hierarchy[site]['children']:
                    point_item = QTreeWidgetItem([point])
                    point_item.setData(0, Qt.UserRole, [site, point])
                    hierarchy[site]['children'][point] = {
                        'item': point_item,
                        'children': {}
                    }
                    hierarchy[site]['item'].addChild(point_item)
                    point_item.setIcon(0, QIcon(":/icon/unfold.png"))

                # 初始化点位下的设备（关键修改：正确关联到对应的point下）
                if device not in hierarchy[site]['children'][point]['children']:
                    device_item = QTreeWidgetItem([device])
                    device_item.setData(0, Qt.UserRole, [site, point, device])
                    hierarchy[site]['children'][point]['children'][device] = {
                        'item': device_item,
                        'children': set()
                    }
                    hierarchy[site]['children'][point]['item'].addChild(device_item)  # 关键修改：添加到对应的point_item下
                    device_item.setIcon(0, QIcon(":/icon/normal.png"))

            self.treeWidget.expandAll()

        except Exception as e:
            logger.critical(f'获取设备列表错误：{e}')
            print('获取设备列表错误:', e)

    # 解析温度数据
    def analyse_temperature_data(self, high_byte, low_byte):
        try:
            value = ((high_byte << 8) | low_byte) / 10.0
            value = None if value == 3276 else value
            return {
                "raw_hex": f"{high_byte:02X}{low_byte:02X}",
                "decimal": value,
                "signed": value if value < 32768 else value - 65536
            }
        except Exception as e:
            print('分析数据错误：', e)
            logger.critical('分析数据错误：', e)

    # 解析设备数据
    def analyse_device_data(self, high_byte, low_byte):
        try:
            # 移除除以10的操作，保持原始值
            value = (high_byte << 8) | low_byte
            value = None if value == 32767 else value  # 修正判断条件为32767
            return {
                "raw_hex": f"{high_byte:02X}{low_byte:02X}",
                "decimal": value,
                "signed": value if value < 32768 else value - 65536
            }
        except Exception as e:
            print('分析数据错误：', e)
            logger.critical('分析数据错误：', e)

    # 数据库查询传感器ID
    def select_sensorID(self):
        try:
            conn = sqlite3.connect(self.resource_path('db/WTC.db'))
            cursor = conn.cursor()

            select_sensorID = """ SELECT sensor_id FROM device_db"""
            cursor.execute(select_sensorID)
            # result = cursor.fetchone()
            # print(result)
        except Exception as e:
            logger.critical(f'获取设备列表错误：{e}')
            print('获取设备列表错误:', e)
        finally:
            # 确保关闭连接
            if conn:
                conn.close()

    def modbus_crc(self, data_bytes):
        crc = 0xFFFF
        for byte in data_bytes:
            crc ^= byte
            for _ in range(8):
                lsb = crc & 0x0001
                crc >>= 1
                if lsb:
                    crc ^= 0xA001
        return crc

    # 改变treeWidget的item指示灯状态
    def change_item_indicator(self, item, state):
        try:
            if state == '正常':
                item.setIcon(0, QIcon(":/icon/normal.png"))
            else:
                item.setIcon(0, QIcon(":/icon/error.png"))
        except Exception as e:
            logger.critical(f'改变指示灯状态错误：{e}')

    # 更新设备状态
    def update_device_status(self, sensor_id, state):
        try:
            conn = sqlite3.connect(self.resource_path('db/WTC.db'))
            cursor = conn.cursor()

            update_device_status = """
                            UPDATE device_db
                            SET state = ?
                            WHERE sensor_id = ?;
                        """
            cursor.execute(update_device_status, (state, sensor_id))
            conn.commit()
            self.query_device_status()

        except Exception as e:
            logger.critical(f'更新设备状态错误：{e}')
            print('更新设备状态错误:', e)

    # 从数据库中查单个子线下面的所有接头的状态
    def query_device_status(self):
        try:
            conn = sqlite3.connect(self.resource_path('db/WTC.db'))
            cursor = conn.cursor()
            select_device_status = """
                        SELECT 
                            site, 
                            location, 
                            device,
                            CASE 
                                WHEN SUM(CASE WHEN state NOT IN ('正常', '环境') THEN 1 ELSE 0 END) > 0 
                                THEN 0  -- 存在异常状态，返回 false
                                ELSE 1  -- 所有状态正常，返回 true
                            END AS all_states_valid
                        FROM device_db
                        GROUP BY site, location, device;
                        """
            cursor.execute(select_device_status)
            results = cursor.fetchall()
            # print(results)

            for result in results:
                item = self.find_tree_item_by_result(result)
                if item:
                    # 根据all_states_valid设置图标状态
                    if result[3]:
                        item.setIcon(0, QIcon(":/icon/normal.png"))
                    else:
                        item.setIcon(0, QIcon(":/icon/alarm.png"))
        except Exception as e:
            logger.critical(f'查询子线状态错误：{e}')
            print('查询子线状态错误:', e)
        finally:
            if conn:
                conn.close()

    # (site, location, device, all_states_valid)查找Tree item
    def find_tree_item_by_result(self, result):
        try:
            site, location, device, all_states_valid = result

            # 获取顶层项目
            for i in range(self.treeWidget.topLevelItemCount()):
                top_item = self.treeWidget.topLevelItem(i)
                if top_item.text(0) == site:
                    # 查找子节点（location）
                    for j in range(top_item.childCount()):
                        location_item = top_item.child(j)
                        if location_item.text(0) == location:
                            # 查找子节点（device）
                            for k in range(location_item.childCount()):
                                device_item = location_item.child(k)
                                if device_item.text(0) == device:
                                    return device_item
            return None
        except Exception as e:
            logger.critical(f'查找TreeWidget项目错误：{e}')
            return None

    # 改变保存状态
    def change_save_flag(self):
        try:
            self.is_save_scv = not self.is_save_scv
            if self.is_save_scv:
                self.pushButton_Roport.setText("关闭保存到表格")
            else:
                self.pushButton_Roport.setText("打开保存到表格")
        except Exception as e:
            print(e)
            logger.critical(f'保存表格错误：{e}')

    # 数据保存到CSV
    def save_temp_date(self, save_time, dates):
        try:
            if not isinstance(dates, (list, tuple)):
                dates = [dates]

            currentday = time.strftime("%Y%m%d", time.localtime())
            filename = './db/' + currentday + '.csv'

            header = ('时间', '温度')
            file_exists = os.path.exists(filename)

            with open(filename, 'a', newline='') as f:
                writer = csv.writer(f)
                if not file_exists:
                    writer.writerow(header)
                for temp_value in dates:
                    writer.writerow([f'\t{save_time}', temp_value])

        except Exception as e:
            print('数据保存到表格错误:', e)
            QMessageBox.critical(self, "错误", "请关闭表格文件后重试", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
            logger.critical(f'数据保存到表格错误：:{e}')

    def stop_sending(self):

        try:
            self.Serial_Thread.stop()
            self.Serial_Thread = None
            self.timer.stop()

            self.sending = False
            # self.pushButton.setText("打开串口")

            if self.serial_port and self.serial_port.is_open:
                self.serial_port.close()
        except Exception as e:
            print("关闭串口错误", e)
            logger.critical(f'关闭串口错误：{e}')
        print("stop")

    def closeEvent(self, event):
        # if self.sending:
        #     self.worker.stop()
        #     self.thread.quit()
        #     self.thread.wait()

        if self.serial_port and self.serial_port.is_open:
            self.serial_port.close()  # 关闭串口
        self.hide_overlay()
        event.accept()
        sys.exit(0)

    # 创建蒙版控件
    def init_overlay(self):
        # 将蒙版放在最外层布局中，确保其层级高于其他控件
        self.overlay = QWidget(self)
        self.overlay.setStyleSheet("background-color: rgba(0, 0, 0, 128);")
        self.overlay.setGeometry(0, 0, self.width(), self.height())
        self.overlay.hide()
        # 确保在布局的最后添加（最高层级）
        self.layout().addWidget(self.overlay)

    def show_overlay(self):
        self.overlay.setGeometry(self.rect())
        self.overlay.raise_()
        self.overlay.show()
        self.overlay.activateWindow()

    def hide_overlay(self):
        # 隐藏蒙版
        self.overlay.hide()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainWindow = MainWindow()
    mainWindow.show()
    sys.exit(app.exec_())
